package cn.org.xiaoweiba.graduationdesign.bookmall.service.impl;

import cn.org.xiaoweiba.graduationdesign.bookmall.annotation.error.ServerError;
import cn.org.xiaoweiba.graduationdesign.bookmall.constant.MailConstant;
import cn.org.xiaoweiba.graduationdesign.bookmall.constant.ResponseResultMessageConstant;
import cn.org.xiaoweiba.graduationdesign.bookmall.constant.UserInfoConstant;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.MailSendInfo;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.UserInfoDto;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.result.BaseResponseResult;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.result.ClientErrorResponseResult;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.result.OkResponseResult;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.result.ServerErrorResponseResult;
import cn.org.xiaoweiba.graduationdesign.bookmall.mapper.UserInfoMapper;
import cn.org.xiaoweiba.graduationdesign.bookmall.po.UserInfo;
import cn.org.xiaoweiba.graduationdesign.bookmall.properties.MailProperties;
import cn.org.xiaoweiba.graduationdesign.bookmall.properties.UserInfoProperties;
import cn.org.xiaoweiba.graduationdesign.bookmall.service.LoginService;
import cn.org.xiaoweiba.graduationdesign.bookmall.utils.*;
import cn.org.xiaoweiba.graduationdesign.bookmall.vo.UserLoginInfoByMailVo;
import cn.org.xiaoweiba.graduationdesign.bookmall.vo.UserLoginInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 登录业务层接口实现类
 */
@Service
public class LoginServiceImpl implements LoginService {

    /**
     * 邮件发送对象
     */
    @Autowired
    private JavaMailSender mailSender;

    /**
     * 邮件配置属性绑定对象
     */
    @Autowired
    private MailProperties mailProperties;

    /**
     * 和 redis 相关的工具类
     */
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 用户信息表持久层对象
     */
    @Autowired
    private UserInfoMapper userInfoMapper;

    /**
     * 和用户信息相关的配置属性绑定对象
     */
    @Autowired
    private UserInfoProperties userInfoProperties;

    /**
     * 判断电子邮箱是否注册过
     *
     * @param email 需要进行判断的电子邮箱
     * @return 结果响应对象
     */
    @Override
    public BaseResponseResult<Object> judgeEmailIsRegistered(String email) {
        // TODO 判断电子邮箱是否为空
        // TODO 判断电子邮箱是否合法
        // TODO 判断电子邮箱是否注册过
        return null;
    }

    /**
     * 发送邮箱验证码。
     * 用户通过输入的邮箱获取登录时使用的验证码。
     *
     * @param mail 邮箱
     * @return 响应结果对象
     */
    @Override
    public BaseResponseResult<Object> sendMailCode(String mail) {
        try {
            // 验证是否可以获取邮箱验证码
            // 尝试从 redis 中获取之前获取过的邮箱验证码
            String codeAgain = redisUtil.getStrVal(MailConstant.getLoginMailCodeAgainRedisKey(mail));
            // 如果之前获取过，且还在再次获取邮箱验证码的等待时间内，直接返回响应结果对象
            if (Objects.nonNull(codeAgain)) {
                return ClientErrorResponseResult.errorResult(
                        ResponseResultMessageConstant.MAIL_CODE_GET_FREQUENTLY,
                        false
                );
            }
            // 得到 HTML 格式的邮件内容模板
            String mailContentHTML = FileUtil.readFileContentByChar(
                    FileUtil.getClassPathFileAbsolute(mailProperties.getCodeContentHTMLPath())
            );
            // 生成 6 位随机数字验证码
            String code = RandomUtil.generateRandomNumStr(6);
            // 将生成的验证码放入 HTML 格式的邮件内容模板中
            mailContentHTML = mailContentHTML.replace(
                    mailProperties.getCodeContentHTMLCodeTemplate(),
                    code
            );
            // 将验证码有效时间放入 HTML 格式的邮件内容模板中
            mailContentHTML = mailContentHTML.replace(
                    mailProperties.getCodeContentHTMLExpireTemplate(),
                    String.valueOf(mailProperties.getCodeExpire() / 60)
            );
            // 发送邮件所需的相关信息
            MailSendInfo mailSendInfo = new MailSendInfo(
                    mailProperties.getDefaultFrom(),
                    mail,
                    mailProperties.getDefaultSubject(),
                    mailContentHTML
            );
            // 发送邮件
            Boolean mailSendResult = MailUtil.sendHTMLMessage(mailSender, mailSendInfo);
            // 发送邮件失败响应
            if (!mailSendResult) {
                return ServerErrorResponseResult.errorResult(ResponseResultMessageConstant.MAIL_CODE_SEND_ERROR);
            }
            // 将生成的验证码保存到 redis 中
            redisUtil.setStrVal(
                    MailConstant.LOGIN_MAIL_CODE_REDIS_KEY.formatted(mail),
                    code,
                    mailProperties.getCodeExpire()
            );
            // 保存由于验证是否可以再次获取邮箱验证码的键值
            redisUtil.setStrVal(
                    MailConstant.LOGIN_MAIL_CODE_AGAIN_REDIS_KEY.formatted(mail),
                    code,
                    mailProperties.getCodeAgain()
            );
            // 发送邮件成功响应
            return OkResponseResult.okResult(
                    ResponseResultMessageConstant.MAIL_CODE_SEND_SUCCESS,
                    true
            );
        } catch (Exception e) {
            return ServerErrorResponseResult.errorResult(
                    ResponseResultMessageConstant.SERVER_ERROR,
                    false
            );
        }
    }

    /**
     * 用户通过电子邮箱进行登录
     *
     * @param userLoginInfoVo 用户登录信息
     * @return 响应结果对象
     */
    @Override
    @ServerError
    public BaseResponseResult<Object> loginByEmail(UserLoginInfoByMailVo userLoginInfoVo) {
        // 尝试从 redis 中获取验证码
        String code = redisUtil.getStrVal(MailConstant.getLoginMailCodeRedisKey(userLoginInfoVo.getMail()));
        // 判断验证码是否失效
        if (Objects.isNull(code)) {
            // 响应邮箱验证码失效
            return ClientErrorResponseResult.errorResult(ResponseResultMessageConstant.MAIL_CODE_EXCEED_THE_TIME_LIMIT);
        }
        // 判断验证码是否输入正确
        if (!code.equals(userLoginInfoVo.getCode())) {
            // 响应邮箱验证码输入错误
            return ClientErrorResponseResult.errorResult(ResponseResultMessageConstant.MAIL_CODE_INPUT_ERROR);
        }
        // 查询账号条件
        QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        // 根据邮箱查询账号
        userInfoQueryWrapper.eq(UserInfoConstant.COLUMN_NAME_MAIL, userLoginInfoVo.getMail());
        // 根据邮箱从数据库中查询账号
        UserInfo userInfo = userInfoMapper.selectOne(userInfoQueryWrapper);
        // 判断是否存在账号
        // 如果账号不存在，进行账号注册，生成新账号
        if (Objects.isNull(userInfo)) {
            // 生成的新账号信息
            userInfo = registerNewAccountByMail(userLoginInfoVo.getMail());
            // 新用户账号信息入库
            int row = userInfoMapper.insert(userInfo);
            // 数据库新增用户信息失败，返回失败响应结果对象
            if (row < 1) {
                return ServerErrorResponseResult.errorResult(
                        ResponseResultMessageConstant.LOGIN_ERROR
                );
            }
        }
        // 修改登录时间
        modifyUserLoginTime(userInfo);
        // 用户信息缓存到 redis
        redisUtil.setStrVal(
                UserInfoConstant.getUserInfoRedisKey(userInfo.idStr()),
                userInfo,
                userInfoProperties.getUserinfoExpire(),
                TimeUnit.DAYS
        );
        // 根据数据库中的用户账号信息转换生成传递给客户端的用户信息
        UserInfoDto userInfoDto = DtoUtil.generateUserInfoDto(userInfo);
        // 响应客户端登录成功结果对象
        return OkResponseResult.okResult(
                ResponseResultMessageConstant.LOGIN_SUCCESS,
                userInfoDto
        );
    }

    /**
     * 修改用户本次的登录时间为当前时间
     *
     * @param userInfo 用户信息
     */
    private void modifyUserLoginTime(UserInfo userInfo) {
        // 将上次登录的时间，保存到"上次登录时间"字段
        userInfo.setLastLoginTime(userInfo.getLoginTime());
        // 将本次的登录时间修改为当前时间
        userInfo.setLoginTime(new Date());
    }

    /**
     * 根据邮箱注册新账号信息
     *
     * @param mail 邮箱
     * @return 新账号信息
     */
    private UserInfo registerNewAccountByMail(String mail) {
        // 新账号信息
        UserInfo userInfo = new UserInfo();
        // 账号
        String accountNumber = generateAccountNumber();
        userInfo.setUsername(accountNumber);
        userInfo.setAccountNumber(accountNumber);
        // 用户密码 SHA256 加密盐为随机的 32 位由十进制数字、大小写字母组成的字符串
        userInfo.setSalt(RandomUtil.generateRandomNumLowerUpperStr(32));
        // 初始密码为空
        userInfo.setPassword(null);
        userInfo.setMail(mail);
        // TODO 用户账号的初始头像
        userInfo.setHeadUrl(null);
        userInfo.setIsAuthentication(false);
        userInfo.setIsBusiness(false);
        // 当前时间
        Date now = new Date();
        userInfo.setLoginTime(now);
        userInfo.setLastLoginTime(now);
        userInfo.setLastModifyUsernameTime(null);
        userInfo.setRegisterTime(now);
        userInfo.setIsDelete(false);
        return userInfo;
    }

    /**
     * 生成账号，xwb_yyMMddHHmmssSSS4位随机数
     *
     * @return 账号
     */
    private String generateAccountNumber() {
        // 保存账号
        StringBuilder accountNumber = new StringBuilder();
        // 生成的账号的格式：xwb_yyMMddHHmmssSSS4位随机数
        accountNumber.append("xwb_");
        accountNumber.append(DateTimeUtil.getNowYear_2());
        accountNumber.append(DateTimeUtil.getNowMouth());
        accountNumber.append(DateTimeUtil.getNowDay());
        accountNumber.append(DateTimeUtil.getNowHour());
        accountNumber.append(DateTimeUtil.getNowMinute());
        accountNumber.append(DateTimeUtil.getNowSecond());
        accountNumber.append(DateTimeUtil.getNowMillisecond());
        accountNumber.append(RandomUtil.generateRandomNumStr(4));
        return accountNumber.toString();
    }


}
